/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Main.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.swing.Icon;
import javax.swing.JApplet;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import com.sun.electric.database.Snapshot;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.id.IdManager;
import com.sun.electric.database.text.Pref;
import com.sun.electric.database.text.Setting;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.technology.Technology;
import com.sun.electric.tool.AbstractUserInterface;
import com.sun.electric.tool.Client;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.Tool;
import com.sun.electric.tool.UserInterfaceInitial;
import com.sun.electric.tool.io.FileType;
import com.sun.electric.tool.io.input.SimulationData;
import com.sun.electric.tool.lang.EvalJavaBsh;
import com.sun.electric.tool.user.ActivityLogger;
import com.sun.electric.tool.user.Clipboard;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.tool.user.MessagesStream;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.UserInterfaceMain;
import com.sun.electric.tool.user.menus.EMenuBar;
import com.sun.electric.tool.user.menus.FileMenu;
import com.sun.electric.tool.user.menus.MenuCommands;
import com.sun.electric.tool.user.ui.MessagesWindow;
import com.sun.electric.tool.user.ui.StatusBar;
import com.sun.electric.tool.user.ui.ToolBar;
import com.sun.electric.tool.user.ui.WindowFrame;
import com.sun.electric.util.PropertiesUtils;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.test.TestByReflection;
/**
* This class initializes Electric and starts the system. How to run Electric:
* <P>
* <P> <CODE>java -jar electric.jar [electric-options]</CODE> without plugins
* <P> <CODE>java -classpath electric.jar<i>delim</i>{list of plugins} com.sun.electric.Launcher [electric-options]</CODE>
* otherwise, where <i>delim</i> is OS-dependant separator
* <P> And Electric options are:
* <P> <CODE> -mdi: multiple document interface mode </CODE>
* <P> <CODE> -sdi: single document interface mode </CODE>
* <P> <CODE> -NOMINMEM: ignore minimum memory provided for JVM </CODE>
* <P> <CODE> -s script name: bean shell script to execute </CODE>
* <P> <CODE> -version: version information </CODE>
* <P> <CODE> -v: brief version information </CODE>
* <P> <CODE> -debug: debug mode. Extra information is available </CODE>
* <P> <CODE> -server: dump strace of snapshots</CODE>
* <P> <CODE> -help: this message </CODE>
* <P> <P>
* See manual for more instructions.
*/
public final class Main extends JApplet
{
private static final long serialVersionUID = 1L;
/**
* Mode of Job manager
*/
private static enum Mode {
/** Thread-safe full screen run. */ FULL_SCREEN_SAFE,
/** JonG: "I think batch mode implies 'no GUI', and nothing more." */ BATCH,
/** Server side. */ SERVER,
/** Client side. */ CLIENT;
}
private static final Mode DEFAULT_MODE = Mode.FULL_SCREEN_SAFE;
private static Mode runMode;
private static Set<Window> modelessDialogs = new HashSet<Window>();
static Main toplevel = null;
public static JDesktopPane desktop = null;
static StatusBar sb = null;
static Dimension scrnSize;
static Dimension appSize;
static MessagesWindow messagesWindow;
static EMenuBar.Instance menuBar;
static ToolBar toolBar;
static int doubleClickDelay;
static Cursor cursor;
static boolean busyCursorOn = false;
/** JonG: "I think batch mode implies 'no GUI', and nothing more." */
public static boolean isBatch() {
return runMode == Mode.BATCH;
}
/**
* The main entry point of Electric.
* @param args the arguments to the program.
*/
public static void main(String[] args) {
JFrame appframe = new JFrame("Christa");
Main applet = new Main();
appframe.getContentPane().add(applet, BorderLayout.CENTER);
applet.init();
applet.start();
appframe.setSize(1024,800);
appframe.setVisible(true);
}
public void init()
{
// convert args to array list
List<String> argsList = new ArrayList<String>();
//for (int i=0; i<args.length; i++) argsList.add(args[i]);
/**
// -v (short version)
if (hasCommandLineOption(argsList, "-v"))
{
System.out.println(Version.getVersion());
System.exit(0);
}
// -version
if (hasCommandLineOption(argsList, "-version"))
{
System.out.println(Version.getApplicationInformation());
System.out.println("\t"+Version.getVersionInformation());
System.out.println("\t"+Version.getCopyrightInformation());
System.out.println("\t"+Version.getWarrantyInformation());
System.exit(0);
}
// -help
if (hasCommandLineOption(argsList, "-help"))
{
System.out.println("Usage (without plugins):");
System.out.println("\tjava -jar electric.jar [electric-options]");
System.out.println("Usage (with plugins):");
System.out.println("\tjava -classpath electric.jar<delim>{list of plugins} com.sun.electric.Launcher [electric-options]");
System.out.println("\t\twhere <delim> is OS-dependant separator (colon or semicolon)");
System.out.println("\nOptions:");
System.out.println("\t-mdi: multiple document interface mode");
System.out.println("\t-sdi: single document interface mode");
System.out.println("\t-NOMINMEM: ignore minimum memory provided for JVM");
System.out.println("\t-s <script name>: bean shell script to execute");
System.out.println("\t-version: version information");
System.out.println("\t-v: brief version information");
System.out.println("\t-debug: debug mode. Extra information is available");
System.out.println("\t-threads <numThreads>: recommended size of thread pool for Job execution.");
System.out.println("\t-logging <filePath>: log server events in a binary file");
System.out.println("\t-socket <socket>: socket port for client/server interaction");
System.out.println("\t-batch: batch mode implies 'no GUI', and nothing more");
System.out.println("\t-server: dump trace of snapshots");
System.out.println("\t-client <machine name>: replay trace of snapshots");
System.out.println("\t-help: this message");
System.exit(0);
}
**/
appSize = getSize();
Toolkit tk = Toolkit.getDefaultToolkit();
scrnSize = tk.getScreenSize();
// -debug for debugging
runMode = DEFAULT_MODE;
List<String> pipeOptions = new ArrayList<String>();
if (hasCommandLineOption(argsList, "-debug")) {
pipeOptions.add(" -debug");
Job.setDebug(true);
}
if (hasCommandLineOption(argsList, "-extraDebug")) {
pipeOptions.add("-extraDebug");
Job.LOCALDEBUGFLAG = true;
}
String numThreadsString = getCommandLineOption(argsList, "-threads");
int numThreads = 0 ;
if (numThreadsString != null) {
numThreads = TextUtils.atoi(numThreadsString);
if (numThreads > 0) {
pipeOptions.add("-threads");
pipeOptions.add(String.valueOf(numThreads));
} else
System.out.println("Invalid option -threads " + numThreadsString);
}
String loggingFilePath = getCommandLineOption(argsList, "-logging");
if (loggingFilePath != null) {
pipeOptions.add("-logging");
pipeOptions.add(loggingFilePath);
}
String socketString = getCommandLineOption(argsList, "-socket");
int socketPort = 0;
if (socketString != null) {
socketPort = TextUtils.atoi(socketString);
if (socketPort > 0) {
pipeOptions.add("-socket");
pipeOptions.add(String.valueOf(socketPort));
} else
System.out.println("Invalid option -socket " + socketString);
}
hasCommandLineOption(argsList, "-NOMINMEM"); // do nothing, just consume option: handled in Launcher
// The server runs in subprocess
if (hasCommandLineOption(argsList, "-pipeserver")) {
ActivityLogger.initialize("electricserver", true, true, true/*false*/, true, false);
Job.pipeServer(numThreads, loggingFilePath, socketPort);
return;
}
ActivityLogger.initialize("electric", true, true, true/*false*/, User.isEnableLog(), User.isMultipleLog());
if (hasCommandLineOption(argsList, "-batch")) {
//System.setProperty("java.awt.headless", "true");
runMode = Mode.BATCH;
}
if (hasCommandLineOption(argsList, "-server")) {
if (runMode != DEFAULT_MODE)
System.out.println("Conflicting thread modes: " + runMode + " and " + Mode.SERVER);
runMode = Mode.SERVER;
}
String serverMachineName = getCommandLineOption(argsList, "-client");
if (serverMachineName != null) {
if (runMode != DEFAULT_MODE)
System.out.println("Conflicting thread modes: " + runMode + " and " + Mode.CLIENT);
runMode = Mode.CLIENT;
}
if (hasCommandLineOption(argsList, "-pipe")) {
if (runMode != DEFAULT_MODE)
System.out.println("Conflicting thread modes: " + runMode + " and " + Mode.CLIENT);
runMode = Mode.CLIENT;
}
if (hasCommandLineOption(argsList, "-pipedebug")) {
if (runMode != DEFAULT_MODE)
System.out.println("Conflicting thread modes: " + runMode + " and " + Mode.CLIENT);
runMode = Mode.CLIENT;
}
desktop = new JDesktopPane();
desktop.setVisible(true);
getContentPane().add(desktop);
AbstractUserInterface ui;
ui = new UserInterfaceMain(argsList, this);
MessagesStream.getMessagesStream();
// initialize database
TextDescriptor.cacheSize();
Tool.initAllTools();
Pref.lockCreation();
EDatabase database = new EDatabase(IdManager.stdIdManager.getInitialSnapshot(), "clientDB");
EDatabase.setClientDatabase(database);
Job.setUserInterface(new UserInterfaceInitial(database));
InitDatabase job = new InitDatabase(argsList);
EDatabase.setServerDatabase(new EDatabase(IdManager.stdIdManager.getInitialSnapshot(), "serverDB"));
EDatabase.setCheckExamine();
Job.initJobManager(numThreads, loggingFilePath, socketPort, ui, job);
}
public void start(){
}
public void stop(){
}
public static void InitializeMessagesWindow() {
messagesWindow = new MessagesWindow(appSize);
}
private static Process invokePipeserver(List<String> electricOptions, boolean withDebugger) throws IOException {
List<String> javaOptions = new ArrayList<String>();
javaOptions.add("-Xss2m");
int maxMemWanted = StartupPrefs.getMemorySize();
javaOptions.add("-Xmx" + maxMemWanted + "m");
long maxPermWanted = StartupPrefs.getPermSpace();
if (maxPermWanted > 0)
javaOptions.add("-XX:MaxPermSize=" + maxPermWanted + "m");
if (withDebugger) {
javaOptions.add("-Xdebug");
javaOptions.add("-Xrunjdwp:transport=dt_socket,server=n,address=localhost:35856");
}
return invokePipeserver(javaOptions, electricOptions);
}
public static class UserInterfaceDummy extends AbstractUserInterface
{
public static final PrintStream stdout = System.out;
public UserInterfaceDummy() {
}
public void startProgressDialog(String type, String filePath) {}
public void stopProgressDialog() {}
public void setProgressValue(int pct) {}
public void setProgressNote(String message) {}
public String getProgressNote() { return null; }
public EDatabase getDatabase() {
return EDatabase.clientDatabase();
}
public EditWindow_ getCurrentEditWindow_() { return null; }
public EditWindow_ needCurrentEditWindow_()
{
System.out.println("Batch mode Electric has no needed windows");
return null;
}
/** Get current cell from current library */
public Cell getCurrentCell()
{
throw new IllegalStateException("Batch mode Electric has no current Cell");
}
public Cell needCurrentCell()
{
throw new IllegalStateException("Batch mode Electric has no current Cell");
}
public void repaintAllWindows() {}
public void adjustReferencePoint(Cell cell, double cX, double cY) {};
public int getDefaultTextSize() { return 14; }
// public Highlighter getHighlighter();
public EditWindow_ displayCell(Cell cell) { return null; }
public void termLogging(final ErrorLogger logger, boolean explain, boolean terminate) {
System.out.println(logger.getInfo());
}
/**
* Method to return the error message associated with the current error.
* Highlights associated graphics if "showhigh" is nonzero.
*/
public String reportLog(ErrorLogger.MessageLog log, boolean showhigh, boolean separateWindow, int position)
{
// return the error message
return log.getMessageString();
}
/**
* Method to show an error message.
* @param message the error message to show.
* @param title the title of a dialog with the error message.
*/
public void showErrorMessage(String message, String title)
{
System.out.println(message);
}
/**
* Method to show an error message.
* @param message the error message to show.
* @param title the title of a dialog with the error message.
*/
public void showErrorMessage(String[] message, String title)
{
System.out.println(message);
}
/**
* Method to show an informational message.
* @param message the message to show.
* @param title the title of a dialog with the message.
*/
public void showInformationMessage(String message, String title)
{
System.out.println(message);
}
private PrintWriter printWriter = null;
/**
* Method print a message.
* @param message the message to show.
* @param newLine add new line after the message
*/
public void printMessage(String message, boolean newLine) {
if (newLine) {
stdout.println(message);
if (printWriter != null)
printWriter.println(message);
} else {
stdout.print(message);
if (printWriter != null)
printWriter.print(message);
}
}
/**
* Method to start saving messages.
* @param filePath file to save
*/
public void saveMessages(final String filePath) {
try
{
if (printWriter != null) {
printWriter.close();
printWriter = null;
}
if (filePath == null) return;
printWriter = new PrintWriter(new BufferedWriter(new FileWriter(filePath)));
} catch (IOException e)
{
System.err.println("Error creating " + filePath);
System.out.println("Error creating " + filePath);
return;
}
System.out.println("Messages will be saved to " + filePath);
}
/**
* Method to show a message and ask for confirmation.
* @param message the message to show.
* @return true if "yes" was selected, false if "no" was selected.
*/
public boolean confirmMessage(Object message) { return true; }
/**
* Method to ask for a choice among possibilities.
* @param message the message to show.
* @param title the title of the dialog with the query.
* @param choices an array of choices to present, each in a button.
* @param defaultChoice the default choice.
* @return the index into the choices array that was selected.
*/
public int askForChoice(String message, String title, String [] choices, String defaultChoice)
{
System.out.println(message + " CHOOSING " + defaultChoice);
for(int i=0; i<choices.length; i++) if (choices[i].equals(defaultChoice)) return i;
return 0;
}
/**
* Method to ask for a line of text.
* @param message the prompt message.
* @param title the title of a dialog with the message.
* @param def the default response.
* @return the string (null if cancelled).
*/
public String askForInput(Object message, String title, String def) { return def; }
@Override
protected void terminateJob(Job.Key jobKey, String jobName, Tool tool,
Job.Type jobType, byte[] serializedJob,
boolean doItOk, byte[] serializedResult, Snapshot newSnapshot) {
printMessage("Job " + jobKey, true);
if (!jobType.isExamine()) {
endChanging();
}
}
@Override
protected void showJobQueue(Job.Inform[] jobQueue) {
printMessage("JobQueue: ", false);
for (Job.Inform jobInfo: jobQueue)
printMessage(" " + jobInfo, false);
printMessage("", true);
}
@Override
protected void addEvent(Client.ServerEvent serverEvent) {
serverEvent.run();
}
}
/** check if command line option 'option' present in
* command line args. If present, return true and remove if from the list.
* Otherwise, return false.
*/
private static boolean hasCommandLineOption(List<String> argsList, String option)
{
for (int i=0; i<argsList.size(); i++) {
if (argsList.get(i).equals(option)) {
argsList.remove(i);
return true;
}
}
return false;
}
/** get command line option for 'option'. Returns null if
* no such 'option'. If found, remove it from the list.
*/
private static String getCommandLineOption(List<String> argsList, String option)
{
for (int i=0; i<argsList.size()-1; i++) {
if (argsList.get(i).equals(option)) {
argsList.remove(i); // note that this shifts objects in arraylist
// check if next string valid (i.e. no dash)
if (argsList.get(i).startsWith("-")) {
System.out.println("Bad command line option: "+ option +" "+ argsList.get(i+1));
return null;
}
return argsList.remove(i);
}
}
return null;
}
/** open any libraries specified on the command line. This method should be
* called after any valid options have been parsed out
*/
public static void openCommandLineLibs(List<String> argsList)
{
for (int i=0; i<argsList.size(); i++) {
String arg = argsList.get(i);
if (arg.startsWith("-")) {
System.out.println("Command line option "+arg+" not understood, ignoring.");
continue;
}
URL url = TextUtils.makeURLToFile(arg);
if (url == null) continue;
if (!isBatch()) User.setWorkingDirectory(TextUtils.getFilePath(url));
// setting database path for future references
FileType.setDatabaseGroupPath(User.getWorkingDirectory());
if (arg.indexOf('.')!=-1 && SimulationData.isKnownSimulationFormatExtension(arg.substring(arg.lastIndexOf('.')+1))) {
SimulationData.plot(null, url, null);
} else {
FileMenu.openLibraryCommand(url);
}
}
}
/**
* Class to init all technologies.
*/
private static class InitDatabase extends Job
{
private Map<String,Object> paramValuesByXmlPath = Technology.getParamValuesByXmlPath();
private List<String> argsList;
private Library mainLib;
private InitDatabase(List<String> argsList)
{
super("Init database", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.argsList = argsList;
}
@Override
public boolean doIt() throws JobException
{
//System.out.println("InitDatabase");
// initialize all of the technologies
Technology.initPreinstalledTechnologies(getDatabase(), paramValuesByXmlPath);
// open no name library first
Library clipLib = Library.newInstance(Clipboard.CLIPBOARD_LIBRAY_NAME, null);
clipLib.setHidden();
Cell clipCell = Cell.newInstance(clipLib, Clipboard.CLIPBOARD_CELL_NAME);
assert clipCell.getId().cellIndex == Clipboard.CLIPBOARD_CELL_INDEX;
clipCell.setTechnology(getTechPool().getGeneric());
mainLib = Library.newInstance("noname", null);
if (mainLib == null) return false;
fieldVariableChanged("mainLib");
mainLib.clearChanged();
return true;
}
@Override
public void terminateOK() {
new InitProjectSettings(argsList).startJobOnMyResult();
User.setCurrentLibrary(mainLib);
}
@Override
public void terminateFail(Throwable jobException) {
System.out.println("Initialization failed");
System.exit(1);
}
}
/**
* Class to init project preferences.
*/
private static class InitProjectSettings extends Job
{
private Setting.SettingChangeBatch changeBatch = new Setting.SettingChangeBatch();
List<String> argsList;
private InitProjectSettings(List<String> argsList)
{
super("Init project preferences", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
Preferences prefRoot = Pref.getPrefRoot();
for (Map.Entry<Setting,Object> e: getDatabase().getSettings().entrySet()) {
Setting setting = e.getKey();
Object value = setting.getValueFromPreferences(prefRoot);
if (value.equals(e.getValue())) continue;
changeBatch.add(setting, value);
}
this.argsList = argsList;
}
@Override
public boolean doIt() throws JobException
{
getDatabase().implementSettingChanges(changeBatch);
return true;
}
@Override
public void terminateOK() {
Job.getExtendedUserInterface().finishInitialization();
String beanShellScript = getCommandLineOption(argsList, "-s");
openCommandLineLibs(argsList);
if (beanShellScript != null)
EvalJavaBsh.runScript(beanShellScript);
}
}
/**
* Class to init project preferences.
*/
private static class InitClient extends Job
{
private Setting.SettingChangeBatch changeBatch = new Setting.SettingChangeBatch();
List<String> argsList;
private InitClient(List<String> argsList)
{
super("Init project preferences", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.argsList = argsList;
}
@Override
public boolean doIt() throws JobException
{
return true;
}
@Override
public void terminateOK() {
Job.getExtendedUserInterface().finishInitialization();
String beanShellScript = getCommandLineOption(argsList, "-s");
openCommandLineLibs(argsList);
if (beanShellScript != null)
EvalJavaBsh.runScript(beanShellScript);
}
}
/**
* Method to return the amount of memory being used by Electric.
* Calls garbage collection and delays to allow completion, so the method is SLOW.
* @return the number of bytes being used by Electric.
*/
public static long getMemoryUsage()
{
collectGarbage();
collectGarbage();
long totalMemory = Runtime.getRuntime().totalMemory();
collectGarbage();
collectGarbage();
long freeMemory = Runtime.getRuntime().freeMemory();
return (totalMemory - freeMemory);
}
private static void collectGarbage()
{
try
{
System.gc();
Thread.sleep(100);
System.runFinalization();
Thread.sleep(100);
} catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
private static final boolean enableAssertions = true;
private static final Logger logger = Logger.getLogger(Main.class.getName());
private static final String[] propertiesToCopy = { "user.home" };
private static String additionalFolder = "additional";
private static String configFile = "econfig.xml";
private static String[] getConfig(String[] args) {
for (String arg : args) {
if (arg.startsWith("-config=")) {
configFile = arg.replaceAll("-config=", "");
args = removeArg(args, arg);
return args;
}
}
return args;
}
private static String[] removeArg(String[] args, String option) {
List<String> tmp = new ArrayList<String>();
Collections.addAll(tmp, args);
tmp.remove(option);
return tmp.toArray(new String[tmp.size()]);
}
@TestByReflection(testMethodName = "getAdditionalFolder")
private static String[] getAdditionalFolder(String[] args) {
for (String arg : args) {
if (arg.startsWith("-additionalfolder=")) {
additionalFolder = arg.replaceAll("-additionalfolder=", "");
args = removeArg(args, arg);
return args;
}
}
return args;
}
/**
* Method to return a String that gives the path to the Electric JAR file.
* If the path has spaces in it, it is quoted.
*
* @return the path to the Electric JAR file.
*/
public static String getJarLocation() {
String jarPath = System.getProperty("java.class.path", ".");
if (jarPath.indexOf(' ') >= 0)
jarPath = "\"" + jarPath + "\"";
return jarPath;
}
private static int getUserInt(String key, int def) {
return Preferences.userNodeForPackage(Main.class).node(StartupPrefs.USER_NODE).getInt(key, def);
}
private static boolean invokeRegression(String[] args) {
List<String> javaOptions = new ArrayList<String>();
List<String> electricOptions = new ArrayList<String>();
electricOptions.add("-debug");
int regressionPos = 0;
if (args[0].equals("-threads")) {
regressionPos = 2;
electricOptions.add("-threads");
electricOptions.add(args[1]);
} else if (args[0].equals("-logging")) {
regressionPos = 2;
electricOptions.add("-logging");
electricOptions.add(args[1]);
} else if (args[0].equals("-debug")) {
regressionPos = 1;
// no need of adding the -debug flag
}
String script = args[regressionPos + 1];
for (int i = regressionPos + 2; i < args.length; i++)
javaOptions.add(args[i]);
Process process = null;
try {
process = invokePipeserver(javaOptions, electricOptions);
} catch (java.io.IOException e) {
logger.log(Level.SEVERE, "Failure starting server subprocess", e);
return false;
}
initClasspath(true);
boolean result = ((Boolean) callByReflection(ClassLoader.getSystemClassLoader(),
"com.sun.electric.tool.Regression", "runScript", new Class[] { Process.class, String.class },
null, new Object[] { process, script })).booleanValue();
return result;
}
public static Process invokePipeserver(List<String> javaOptions, List<String> electricOptions)
throws IOException {
List<String> procArgs = new ArrayList<String>();
String program = "java";
String javaHome = System.getProperty("java.home");
if (javaHome != null)
program = javaHome + File.separator + "bin" + File.separator + program;
procArgs.add(program);
procArgs.add("-ea");
procArgs.add("-Djava.util.prefs.PreferencesFactory=com.sun.electric.database.text.EmptyPreferencesFactory");
procArgs.addAll(javaOptions);
procArgs.add("-cp");
procArgs.add(getJarLocation());
procArgs.add("com.sun.electric.Launcher");
procArgs.addAll(electricOptions);
procArgs.add("-pipeserver");
String command = "";
for (String arg : procArgs)
command += " " + arg;
logger.log(Level.INFO, "exec: {0}", new Object[] { command });
return new ProcessBuilder(procArgs).start();
}
private static ClassLoader pluginClassLoader;
// public static Class classFromPlugins(String className) throws ClassNotFoundException {
// return pluginClassLoader.loadClass(className);
// }
// private static void loadAndRunMain(String[] args, boolean loadDependencies) {
// args = getAdditionalFolder(args);
// args = getConfig(args);
//
// Configuration.setConfigName(configFile);
//
// initClasspath(loadDependencies);
// callByReflection(pluginClassLoader, "com.sun.electric.Main", "main", new Class[] { String[].class },
// null, new Object[] { args });
// }
private static void initClasspath(boolean loadDependencies) {
ClassLoader launcherLoader = Main.class.getClassLoader();
pluginClassLoader = launcherLoader;
if (loadDependencies) {
initClasspath(readAdditionalFolder(), launcherLoader);
initClasspath(readMavenDependencies(), launcherLoader);
// pluginClassLoader = new EClassLoader(pluginJars, appLoader);
}
if (enableAssertions) {
launcherLoader.setDefaultAssertionStatus(true);
pluginClassLoader.setDefaultAssertionStatus(true);
}
}
private static void initClasspath(URL[] urls, ClassLoader launcherLoader) {
for (URL url : urls) {
Object result = callByReflection(launcherLoader, "java.net.URLClassLoader", "addURL",
new Class[]{URL.class}, launcherLoader, new Object[]{url});
}
}
private static URL[] readAdditionalFolder() {
List<URL> urls = new ArrayList<URL>();
File additionalFolder = new File("additional");
if (additionalFolder.exists() && additionalFolder.isDirectory()) {
File[] files = additionalFolder.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith(".jar");
}
});
for (File file : files) {
try {
urls.add(file.toURI().toURL());
logger.info("Add " + file.getAbsoluteFile() + " to classpath...");
} catch (MalformedURLException e) {
logger.log(Level.SEVERE, "Add " + file.getAbsoluteFile() + " to classpath...", e);
}
}
}
return urls.toArray(new URL[urls.size()]);
}
private static URL[] readMavenDependencies() {
String mavenDependenciesResourceName = "maven.dependencies";
URL mavenDependencies = ClassLoader.getSystemResource(mavenDependenciesResourceName);
if (mavenDependencies == null) {
return new URL[0];
}
List<URL> urls = new ArrayList<URL>();
List<File> repositories = new ArrayList<File>();
String mavenRepository = ".m2" + File.separator + "repository";
String homeDirProp = System.getProperty("user.home");
if (homeDirProp != null) {
File homeDir = new File(homeDirProp);
File file = new File(homeDir, mavenRepository);
if (file.canRead()) {
repositories.add(file);
logger.log(Level.FINE, "Found repository {0}", file);
} else if (file.exists()) {
logger.log(Level.FINE, "Can't read repository {0}", file);
} else {
logger.log(Level.FINE, "Not found repository {0}", file);
}
}
if (mavenDependencies.getProtocol().equals("jar")) {
String path = mavenDependencies.getPath();
if (path.startsWith("file:") && path.endsWith("!/" + mavenDependenciesResourceName)) {
String jarPath = path.substring("file:".length(), path.length() - "!/".length()
- mavenDependenciesResourceName.length());
File jarDir = new File(jarPath).getParentFile();
File file = new File(jarDir, mavenRepository);
if (file.canRead()) {
repositories.add(file);
logger.log(Level.FINE, "Found repository {0}", file);
} else if (file.exists()) {
logger.log(Level.FINE, "Can't read repository {0}", file);
} else {
logger.log(Level.FINE, "Not found repository {0}", file);
}
}
}
try {
Properties properties = PropertiesUtils.load(mavenDependenciesResourceName);
for (Object dependency : properties.keySet()) {
for (File repository : repositories) {
File file = new File(repository, dependency.toString());
if (file.canRead()) {
URL url = file.toURI().toURL();
urls.add(url);
logger.log(Level.FINE, "Add {0} to class path", url);
break;
} else if (file.exists()) {
logger.log(Level.FINE, "Can't read {0}", file);
} else {
logger.log(Level.FINEST, "Not found {0}", file);
}
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error reading maven dependencies", e);
}
return urls.toArray(new URL[urls.size()]);
}
private static Object callByReflection(ClassLoader classLoader, String className, String methodName,
Class[] argTypes, Object obj, Object[] argValues) {
try {
Class mainClass = classLoader.loadClass(className);
Method method = mainClass.getDeclaredMethod(methodName, argTypes);
method.setAccessible(true);
return method.invoke(obj, argValues);
} catch (ClassNotFoundException e) {
logger.log(Level.SEVERE, "Can't invoke Electric", e);
} catch (NoSuchMethodException e) {
logger.log(Level.SEVERE, "Can't invoke Electric", e);
} catch (IllegalAccessException e) {
logger.log(Level.SEVERE, "Can't invoke Electric", e);
} catch (InvocationTargetException e) {
logger.log(Level.SEVERE, "Error in Electric invocation", e.getTargetException());
}
return null;
}
private static class EClassLoader extends URLClassLoader {
private EClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith("com.sun.electric.plugins.") || name.startsWith("com.sun.electric.scala.")) {
Class c = findLoadedClass(name);
if (c == null) {
String path = name.replace('.', '/').concat(".class");
URL url = getResource(path);
if (url != null) {
try {
DataInputStream in = new DataInputStream(url.openStream());
int len = in.available();
byte[] ba = new byte[len];
in.readFully(ba);
in.close();
c = defineClass(name, ba, 0, len);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
} else {
throw new ClassNotFoundException(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
return super.loadClass(name, resolve);
}
}
/** Get the Menu Bar. Unfortunately named because getMenuBar() already exists */
public static EMenuBar getEMenuBar() { return menuBar.getMenuBarGroup(); }
public static synchronized List<EMenuBar.Instance> getMenuBars() {
ArrayList<EMenuBar.Instance> menuBars = new ArrayList<EMenuBar.Instance>();
for (Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) {
// WindowFrame wf = it.next();
// menuBars.add(wf.getFrame().getTheMenuBar());
}
return menuBars;
}
public static Icon getFrameIcon()
{
return null;
}
public static void InitializeWindows(Main thing) {
try{
menuBar = MenuCommands.menuBar().genInstance();
} catch (Exception e)
{
e.printStackTrace();
}
thing.setJMenuBar(menuBar);
// create the tool bar
Main.toolBar = new ToolBar();
thing.getContentPane().add(toolBar, BorderLayout.NORTH);
// create the status bar
thing.sb = new StatusBar(null, true);
thing.getContentPane().add(thing.sb, BorderLayout.SOUTH);
WindowFrame.createEditWindow(null);
toplevel = thing;
}
public static Dimension getScreenSize()
{
return new Dimension(scrnSize);
}
public static MessagesWindow getMessagesWindow() { return messagesWindow; }
public static Point getCursorLocation() {
return toplevel.getLocationOnScreen();
}
public static Component getCurrentJFrame() {
// TODO Auto-generated method stub
return null;
}
/**
* The busy cursor overrides any other cursor.
* Call clearBusyCursor to reset to last set cursor
*/
public static synchronized void setBusyCursor(boolean on) {
if (on) {
if (!busyCursorOn)
setCurrentCursorPrivate(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
busyCursorOn = true;
} else {
// if the current cursor is a busy cursor, set it to the last normal cursor
if (busyCursorOn)
setCurrentCursorPrivate(getCurrentCursor());
busyCursorOn = false;
}
}
public static Cursor getCurrentCursor() { return cursor; }
public static synchronized void setCurrentCursor(Cursor newcursor)
{
cursor = newcursor;
setCurrentCursorPrivate(cursor);
}
private static synchronized void setCurrentCursorPrivate(Cursor cursor)
{
for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); )
{
WindowFrame wf = it.next();
wf.setCursor(cursor);
}
}
/**
* Method to add an internal frame to the desktop.
* This only makes sense in MDI mode, where the desktop has multiple subframes.
* @param jif the internal frame to add.
*/
public static void addToDesktop(JInternalFrame jif)
{
if (desktop.isVisible() && !Job.isClientThread())
SwingUtilities.invokeLater(new ModifyToDesktopSafe(jif, true)); else
(new ModifyToDesktopSafe(jif, true)).run();
}
/**
* Method to remove an internal frame from the desktop.
* This only makes sense in MDI mode, where the desktop has multiple subframes.
* @param jif the internal frame to remove.
*/
public static void removeFromDesktop(JInternalFrame jif)
{
if (desktop.isVisible() && !Job.isClientThread())
SwingUtilities.invokeLater(new ModifyToDesktopSafe(jif, false)); else
(new ModifyToDesktopSafe(jif, false)).run();
}
private static class ModifyToDesktopSafe implements Runnable
{
private JInternalFrame jif;
private boolean add;
private ModifyToDesktopSafe(JInternalFrame jif, boolean add) { this.jif = jif; this.add = add; }
public void run()
{
if (add)
{
desktop.add(jif);
try
{
jif.show();
} catch (ClassCastException e)
{
// Jake Baker keeps getting a ClassCastException here, so for now, let's catch it
System.out.println("ERROR: Could not show new window: " + e.getMessage());
}
} else
{
desktop.remove(jif);
}
}
}
/** Get the Menu Bar. Unfortunately named because getMenuBar() already exists */
public static EMenuBar.Instance getTheMenuBar() { return menuBar; }
/**
* Get the tool bar associated with this TopLevel
* @return the ToolBar.
*/
public static ToolBar getToolBar() { return toolBar; }
public static synchronized List<ToolBar> getToolBars() {
ArrayList<ToolBar> toolBars = new ArrayList<ToolBar>();
for (Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); ) {
WindowFrame wf = it.next();
// toolBars.add(wf.getFrame().getToolBar());
}
return toolBars;
}
/**
* Method to return status bar associated with this TopLevel.
* @return the status bar associated with this TopLevel.
*/
public static StatusBar getStatusBar() { return sb; }
/**
* Method to return the speed of double-clicks (in milliseconds).
* @return the speed of double-clicks (in milliseconds).
*/
public static int getDoubleClickSpeed() { return doubleClickDelay; }
/**
* Method to return a list of possible window areas.
* On MDI systems, there is just one window areas.
* On SDI systems, there is one window areas for each display head on the computer.
* @return an array of window areas.
*/
public static Rectangle [] getWindowAreas()
{
Rectangle [] areas;
areas = getDisplays();
return areas;
}
/**
* Method to return a list of display areas, one for each display head on the computer.
* @return an array of display areas.
*/
public static Rectangle [] getDisplays()
{
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice [] gs = ge.getScreenDevices();
Rectangle [] areas = new Rectangle[gs.length];
for (int j = 0; j < gs.length; j++)
{
GraphicsDevice gd = gs[j];
GraphicsConfiguration gc = gd.getDefaultConfiguration();
areas[j] = gc.getBounds();
}
return areas;
}
public static void addModelessDialog(Window dialog) { modelessDialogs.add(dialog); }
public static void removeModelessDialog(JFrame dialog) { modelessDialogs.remove(dialog); }
}